package cn.itcast.nems.journal.service.impl;

import cn.itcast.ic.common.bean.Page;
import cn.itcast.nems.base.dao.entity.E2Clazz;
import cn.itcast.nems.base.dao.mapper.E2ClazzMapper;
import cn.itcast.nems.common.BasePageRequest;
import cn.itcast.nems.common.account.AccountHolder;
import cn.itcast.nems.common.tenant.TenantInfo;
import cn.itcast.nems.employee.dao.bean.EmployeeGroupBean;
import cn.itcast.nems.employee.dao.entity.Employee;
import cn.itcast.nems.employee.service.EmployeeService;
import cn.itcast.nems.journal.dto.*;
import cn.itcast.nems.journal.service.JournalTransferCommonService;
import cn.itcast.nems.journal.service.JournalTransferReasonServiceV2;
import cn.itcast.nems.journal.service.JournalTransferService;
import cn.itcast.nems.journal.service.JournalTransferSuspendedService;
import cn.itcast.nems.oldems.dao.entiry.Student;
import cn.itcast.nems.oldems.dao.mapper.StudentMapper;
import cn.itcast.nems.product.scheduler.TuitionFeeService;
import cn.itcast.nems.studentrecord.bo.EditSuspendJournalParams;
import cn.itcast.nems.studentrecord.bo.SuspendClazzParams;
import cn.itcast.nems.studentrecord.bo.SuspendJournalQuery;
import cn.itcast.nems.studentrecord.dao.condition.SuspendStudentQuery;
import cn.itcast.nems.studentrecord.dao.dataobject.JournalTransferAndLineId;
import cn.itcast.nems.studentrecord.dao.entity.*;
import cn.itcast.nems.studentrecord.dao.mapper.JournalTransferLineMapper;
import cn.itcast.nems.studentrecord.dao.mapper.JournalTransferMapper;
import cn.itcast.nems.studentrecord.dao.mapper.StudentRecordMapper;
import cn.itcast.nems.studentrecord.dao.mapper.StudentRecordStudentRelationshipMapper;
import cn.itcast.nems.studentrecord.dto.SuspendJournalVo;
import cn.itcast.nems.studentrecord.dto.SuspendStudent;
import cn.itcast.nems.studentrecord.dto.SuspendedClazz;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static cn.itcast.nems.journal.util.TransferUtils.formatAccount;
import static cn.itcast.nems.journal.util.TransferUtils.formatPrice;

@Service
@RequiredArgsConstructor
public class JournalTransferSuspendedServiceImpl implements JournalTransferSuspendedService {

    private final E2ClazzMapper e2ClazzMapper;
    private final StudentRecordMapper studentRecordMapper;
    private final JournalTransferService journalTransferService;
    private final JournalTransferReasonServiceV2 reasonService;
    private final JournalTransferMapper journalTransferMapper;
    private final JournalTransferLineMapper journalTransferLineMapper;
    private final StudentRecordStudentRelationshipMapper studentRecordStudentRelationshipMapper;
    private final StudentMapper studentMapper;
    private final TuitionFeeService tuitionFeeService;
    private final JournalTransferCommonService commonService;
    private final EmployeeService employeeService;

    @Override
    public SuspendedClazz suspendedClazz(SuspendClazzParams params) {
        var studentRecord = studentRecord(params.getOriginStudentRecordId());
        var srcClazz = studentRecordClazz(studentRecord);
        if (srcClazz != null) {
            var targetClazz = suspendClazz(srcClazz);
            if (targetClazz != null) {
                var suspendClazz = new SuspendedClazz();
                suspendClazz.setClazzId(targetClazz.getClazzId());
                suspendClazz.setClazzName(targetClazz.getClazzName());
                suspendClazz.setGraduationDate(targetClazz.getGraduationDate());
                suspendClazz.setPredictionGraduationDate(targetClazz.getPredictionGraduationDate());
                suspendClazz.setStartDate(targetClazz.getStartDate());
                return suspendClazz;
            }
        }
        return null;
    }

    @Override
    public SuspendJournalVo suspendPreview(EditSuspendJournalParams params) {
        var studentRecord = studentRecordMapper.selectById(params.getOriginStudentRecordId());
        Assert.notNull(studentRecord, "学籍不存在");
        var srcClazz = studentRecordClazz(studentRecord);
        return setAmounts(studentRecord, srcClazz, params.getOriginClazzOutDate());
    }

    @Override
    public SuspendJournalVo editSuspendJournal(EditSuspendJournalParams params) {
        if ("COMMIT".equals(params.getState())) {
            Assert.notNull(params.getId(), "休学转班记录ID不能为空");
            final var bo = new JournalTransferInfoBDto();
            bo.setId(params.getId());
            bo.setState("COMMIT");
            journalTransferService.update(bo);
            var response = new SuspendJournalVo();
            response.setId(bo.getId());
            return response;
        }
        var studentRecord = studentRecordMapper.selectById(params.getOriginStudentRecordId());
        var srcClazz = studentRecordClazz(studentRecord);
        E2Clazz targetClazz;
        if ("休学".equals(params.getDirection())) {
            targetClazz = suspendClazz(srcClazz);
        } else if ("休学归来".equals(params.getDirection())) {
            Assert.notNull(params.getTargetClazzId(), "休学归来目标班级ID不能为空");
            targetClazz = e2ClazzMapper.selectOne(new QueryWrapper<E2Clazz>()
                    .lambda()
                    .eq(E2Clazz::getClazzId, params.getTargetClazzId())
                    .last("LIMIT 1"));
            Assert.notNull(targetClazz, "休学归来目标班级不存在");
        } else {
            throw new IllegalArgumentException("direction参数不正确, 只能为休学或休学归来, 不能为:" + params.getDirection());
        }
        var bo = getJournalTransferCreateBo(params, targetClazz);
        bo.setSingleLine(getJournalTransferCreateBoSingleLine(
                params,
                studentRecord,
                srcClazz,
                reasonService.findReasonByCodeOrNull(params.getReasonCode()),
                targetClazz
        ));
        if (!StringUtils.hasText(params.getId())) {
            var id = journalTransferService.create(bo);
            return SuspendJournalVo.builder()
                    .id(id).build();
        } else {
            var id = journalTransferService.updateSingleLine(bo);
            return SuspendJournalVo.builder()
                    .id(id).build();
        }
    }

    @Override
    public SuspendJournalVo info(String id) {
        var journal = journalTransferMapper.selectById(id);
        var groups = AccountHolder.getRequiredAccountEmployeeGroups();
        boolean hasPermission = false;
        for (EmployeeGroupBean g : groups.getGroupList()) {
            hasPermission = g.getSchoolCodeList().contains(journal.getTargetClazzCampusId())
                    && g.getSubjectIdList().contains(journal.getTargetClazzSubjectId());
            if (hasPermission) break;
        }
        if (!hasPermission) {
            // todo : 判断代办or已办则不抛异常 , 但是那样太慢了
            // BizExceptionProducer.throwProduce(NemsErrorBodyEnum.FORBIDDEN);
        }
        Assert.notNull(journal, "休学转班记录不存在");
        var line = journalTransferLineMapper.selectOne(new QueryWrapper<JournalTransferLine>()
                .lambda()
                .eq(JournalTransferLine::getJournalTransferId, id));
        Assert.notNull(line, "休学转班记录行不存在");
        E2Clazz targetClazz = e2ClazzMapper.selectOne(new QueryWrapper<E2Clazz>()
                .lambda()
                .eq(E2Clazz::getClazzId, journal.getTargetClazzId()));
        E2Clazz cz = e2ClazzMapper.selectOne(new QueryWrapper<E2Clazz>()
                .lambda()
                .eq(E2Clazz::getClazzId, line.getOriginClazzId()));
        Assert.notNull(targetClazz, "休学班级不存在");
        var record = studentRecordMapper.selectById(line.getOriginStudentRecordId());
        Assert.notNull(record, "学籍不存在");
        var relationship = studentRecordStudentRelationshipMapper.selectOne(new QueryWrapper<StudentRecordStudentRelationship>()
                .lambda()
                .eq(StudentRecordStudentRelationship::getStudentRecordId, record.getId())
                .last("LIMIT 1"));
        Assert.notNull(relationship, "学籍学生关系不存在");
        var student = studentMapper.selectById(relationship.getStudentId());
        Assert.notNull(student, "学生不存在");
        //
        var account = employeeService.findOneByEmail(journal.getCreatedBy());
        var vo = new SuspendJournalVo();
        vo.setId(journal.getId());
        vo.setCreatedDatetime(journal.getCreatedDatetime());
        vo.setState(journal.getState());
        vo.setCreatedBy(journal.getCreatedBy());
        if (account != null) {
            vo.setCreatedByName(account.getName());
            vo.setCreatedByAndName(formatAccount(account, journal.getCreatedBy()));
        }
        if (targetClazz.getSuspensionFlag()) {
            vo.setSuspendTypeName("休学");
        } else {
            vo.setSuspendTypeName("休学归来");
        }
        vo.setTargetClazzId(targetClazz.getClazzId());
        vo.setTargetClazzName(targetClazz.getClazzName());
        vo.setOriginClazzTypeId(targetClazz.getClazzTypeId());
        vo.setTargetClazzPrice(line.getTargetClazzAmountDue());
        vo.setTargetClazzSubjectId(targetClazz.getSubjectId());
        vo.setTargetClazzSubjectName(targetClazz.getSubjectName());
        vo.setTargetClazzCampusId(targetClazz.getSchoolCode());
        vo.setTargetClazzCampusName(targetClazz.getSchoolName());
        vo.setTargetClazzModelId(targetClazz.getTeachingModel());
        // vo.setTargetClazzModelName(journal.getTeachingModelName());
        vo.setTargetClazzInDate(line.getTargetClazzInDate());
        vo.setRemark(journal.getRemark());
        vo.setOriginStudentRecordId(line.getOriginStudentRecordId());
        vo.setOriginClazzId(line.getOriginClazzId());
        vo.setOriginClazzName(line.getOriginClazzName());
        vo.setOriginClazzTypeId(targetClazz.getClazzTypeId());  // 减少一次查询，所以直接赋值，如果原班级能编辑
        vo.setOriginClazzAmountDueFormatted(formatPrice(line.getOriginClazzAmountDue()));
        vo.setOriginClazzServiceDays(line.getOriginClazzServiceDays());
        vo.setOriginClazzActualSettlementDays(line.getOriginClazzActualSettlementDays());
        vo.setOriginClazzActualSettlementAmountFormatted(formatPrice(line.getOriginClazzActualSettlementAmount()));
        vo.setOriginClazzOutDate(line.getOriginClazzOutDate());
        vo.setReasonCode(line.getReasonCode());
        vo.setReasonDescription(line.getReasonDescription());
        vo.setStudentId(student.getId());
        vo.setStudentNumber(student.getNumber());
        vo.setStudentName(student.getName());
        vo.setPaidAmountFormatted(formatPrice(record.getPaidAmount()));
        vo.setFreezeAmountFormatted(formatPrice(line.getTargetClazzTransferAmount()));
        vo.setFormId(journalTransferService.loadFormId(vo.getId()));
        vo.setSpecialFlag(journal.getSpecialFlag());
        vo.setOriginClazzGraduationDate(cz.getGraduationDate());
        vo.setOriginClazzPredictionGraduationDate(cz.getPredictionGraduationDate());
        vo.setTargetClazzGraduationDate(targetClazz.getGraduationDate());
        vo.setTargetClazzPredictionGraduationDate(targetClazz.getPredictionGraduationDate());
        vo.setOriginClazzInDate(record.getInDate().toLocalDate());
        vo.setTargetClazzStartDate(targetClazz.getStartDate());

        vo.setMultiPaymentAccountFlag(line.getMultiPaymentAccountFlag());
        vo.setOriginPaymentAccountCode(line.getOriginPaymentAccountCode());
        vo.setOriginPaymentAccountName(line.getOriginPaymentAccountName());

        vo.setTargetClazzUpgradeable(targetClazz.getUpgradeable());
        vo.setOriginClazzUpgradeable(cz.getUpgradeable());

        return vo;
    }

    @Override
    public Page<SuspendJournalVo> pageList(BasePageRequest<SuspendJournalQuery> queryParam) {
        var journalTransferQuery = new JournalTransferQuery();
        journalTransferQuery.setTypes(List.of(JournalTransferType.SUSPENSION));
        journalTransferQuery.setApprovalType(queryParam.getCondition().getApprovalType());
        journalTransferQuery.setLineStudentName(queryParam.getCondition().getLineStudentName());
        journalTransferQuery.setCreatedStartDate(queryParam.getCondition().getCreatedStartDate());
        journalTransferQuery.setCreatedEndDate(queryParam.getCondition().getCreatedEndDate());
        journalTransferQuery.setState(queryParam.getCondition().getState());
        journalTransferQuery.setCreatedBy(queryParam.getCondition().getCreatedBy());
        journalTransferQuery.setId(queryParam.getCondition().getJournalId());
        journalTransferQuery.setDirection(queryParam.getCondition().getDirection());
        journalTransferQuery.setReasonCode(queryParam.getCondition().getReasonCode());
        journalTransferQuery.setOriginClazzId(queryParam.getCondition().getOriginClazzId());
        journalTransferQuery.setOriginClazzName(queryParam.getCondition().getOriginClazzName());
        journalTransferQuery.setTargetClazzName(queryParam.getCondition().getTargetClazzName());
        journalTransferQuery.setStudentNumber(queryParam.getCondition().getStudentNumber());
        var journalPage = journalTransferService.page(
                journalTransferQuery,
                new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(queryParam.getPageNumber(), queryParam.getPageSize())
        );
        return mapJournalPage(journalPage);
    }

    private Page<SuspendJournalVo> mapJournalPage(Page<JournalTransferInListVDto> journalPage) {
        return Page.of(
                journalPage.getPageNumber(),
                journalPage.getPageSize(),
                journalPage.getRecordCount(),
                mapJournalList(journalPage.getRecords())
        );
    }

    private List<SuspendJournalVo> mapJournalList(List<JournalTransferInListVDto> records) {
        if (records == null) {
            return null;
        }
        if (records.isEmpty()) {
            return List.of();
        }
        var list = new java.util.ArrayList<SuspendJournalVo>(records.size());
        var journalIds = records.stream().map(JournalTransferInListVDto::getId).collect(Collectors.toList());
        var lineMap = journalTransferLineMapper.selectList(new QueryWrapper<JournalTransferLine>()
                        .lambda()
                        .in(JournalTransferLine::getJournalTransferId, journalIds))
                .stream().collect(Collectors.toMap(JournalTransferLine::getJournalTransferId, x -> x));
        var accountIdSet = records.stream().map(JournalTransferInListVDto::getCreatedBy).collect(Collectors.toSet());
        var accountMap = accountIdSet.stream().distinct().map(employeeService::findOneByEmail).collect(Collectors.toMap(Employee::getEmail, x -> x));
        var clazzIdSet = Stream.of(
                records.stream().map(JournalTransferInListVDto::getTargetClazzId),
                lineMap.values().stream().map(JournalTransferLine::getOriginClazzId)
        ).flatMap(x -> x).collect(Collectors.toSet());
        var clazzMap = e2ClazzMapper.selectList(new QueryWrapper<E2Clazz>()
                        .lambda()
                        .in(E2Clazz::getClazzId, clazzIdSet))
                .stream().collect(Collectors.toMap(E2Clazz::getClazzId, x -> x));
        var originStudentRecordIdSet = lineMap.values().stream().map(JournalTransferLine::getOriginStudentRecordId).collect(Collectors.toSet());
        var studentRecordMap = studentRecordMapper.selectList(new QueryWrapper<StudentRecord>()
                        .lambda()
                        .in(StudentRecord::getId, originStudentRecordIdSet))
                .stream().collect(Collectors.toMap(StudentRecord::getId, x -> x));
        var relationshipMap = studentRecordStudentRelationshipMapper.selectList(new QueryWrapper<StudentRecordStudentRelationship>()
                        .lambda()
                        .in(StudentRecordStudentRelationship::getStudentRecordId, originStudentRecordIdSet))
                .stream().collect(Collectors.toMap(StudentRecordStudentRelationship::getStudentRecordId, x -> x));
        var studentIdSet = relationshipMap.values().stream().map(StudentRecordStudentRelationship::getStudentId).collect(Collectors.toSet());
        var studentMap = studentMapper.selectList(new QueryWrapper<Student>()
                        .lambda()
                        .in(Student::getId, studentIdSet))
                .stream().collect(Collectors.toMap(Student::getId, x -> x));
        for (JournalTransferInListVDto journal : records) {
            var line = lineMap.get(journal.getId());
            var account = accountMap.get(journal.getCreatedBy());
            var targetClazz = clazzMap.get(journal.getTargetClazzId());
            var record = studentRecordMap.get(line.getOriginStudentRecordId());
            var relationship = relationshipMap.get(line.getOriginStudentRecordId());
            var student = studentMap.get(relationship.getStudentId());
            var vo = new SuspendJournalVo();
            vo.setId(journal.getId());
            vo.setCreatedDatetime(journal.getCreatedDatetime());
            vo.setTargetClazzId(journal.getTargetClazzId());
            vo.setTargetClazzName(journal.getTargetClazzName());
            vo.setState(journal.getState());
            vo.setCreatedBy(journal.getCreatedBy());
            if (account != null) {
                vo.setCreatedByName(account.getName());
                vo.setCreatedByAndName(formatAccount(account, journal.getCreatedBy()));
            }
            vo.setSuspendTypeName(journal.getDirection());
            vo.setTargetClazzId(targetClazz.getClazzId());
            vo.setTargetClazzName(targetClazz.getClazzName());
            vo.setRemark(journal.getRemark());
            vo.setOriginStudentRecordId(line.getOriginStudentRecordId());
            vo.setOriginClazzId(line.getOriginClazzId());
            vo.setOriginClazzName(line.getOriginClazzName());
            vo.setOriginClazzAmountDueFormatted(formatPrice(line.getOriginClazzAmountDue()));
            vo.setOriginClazzServiceDays(line.getOriginClazzServiceDays());
            vo.setOriginClazzActualSettlementDays(line.getOriginClazzActualSettlementDays());
            vo.setOriginClazzActualSettlementAmountFormatted(formatPrice(line.getOriginClazzActualSettlementAmount()));
            vo.setOriginClazzOutDate(line.getOriginClazzOutDate());
            vo.setReasonCode(line.getReasonCode());
            vo.setReasonDescription(line.getReasonDescription());
            vo.setStudentId(student.getId());
            vo.setStudentNumber(student.getNumber());
            vo.setStudentName(student.getName());
            vo.setPaidAmountFormatted(formatPrice(record.getPaidAmount()));
            vo.setFreezeAmountFormatted(formatPrice(line.getTargetClazzTransferAmount()));
            vo.setApprovalable(journal.getApprovalable());
            vo.setFormId(journal.getFormId());
            vo.setSpecialFlag(journal.getSpecialFlag());
            vo.setCategory(journal.getCategory());
            list.add(vo);
        }
        return list;
    }

    @Override
    public Page<SuspendStudent> studentPageList(BasePageRequest<SuspendStudentQuery> queryParam) {
        E2Clazz targetClazz = null;
        if (queryParam.getCondition().getTargetClazzId() != null) {
            targetClazz = e2ClazzMapper.selectOne(new QueryWrapper<E2Clazz>()
                    .lambda()
                    .eq(E2Clazz::getClazzId, queryParam.getCondition().getTargetClazzId())
                    .last("LIMIT 1"));
        }
        var recordPage = studentRecordMapper.findStudentRecordPage(
                new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(queryParam.getPageNumber(), queryParam.getPageSize()),
                queryParam.getCondition()
        );
        return mapPageRecordToSuspendStudent(recordPage, targetClazz);
    }

    Page<SuspendStudent> mapPageRecordToSuspendStudent(com.baomidou.mybatisplus.extension.plugins.pagination.Page<StudentRecord> recordPage, E2Clazz targetClazz) {
        return Page.of(
                (int) recordPage.getCurrent(),
                (int) recordPage.getSize(),
                (int) recordPage.getTotal(),
                mapRecordToSuspendStudent(recordPage.getRecords(), targetClazz)
        );
    }

    List<SuspendStudent> mapRecordToSuspendStudent(List<StudentRecord> records, E2Clazz targetClazz) {
        if (records.isEmpty()) {
            return List.of();
        }
        var recordIds = records.stream().map(StudentRecord::getId).toList();
        var recordRelationships = studentRecordStudentRelationshipMapper.selectList(new QueryWrapper<StudentRecordStudentRelationship>()
                .lambda()
                .in(StudentRecordStudentRelationship::getStudentRecordId, recordIds));
        var studentIds = recordRelationships.stream().map(StudentRecordStudentRelationship::getStudentId).distinct().toList();
        var students = studentMapper.selectList(new QueryWrapper<Student>()
                .lambda()
                .in(Student::getId, studentIds));
        var lines = journalTransferLineMapper.selectList(new QueryWrapper<JournalTransferLine>()
                .lambda()
                .in(JournalTransferLine::getStudentRecordId, recordIds));
        List<String> originRecordIds;
        List<StudentRecord> originRecords;
        List<String> transferIds;
        List<JournalTransfer> transfers;
        List<Integer> originClazzIds;
        List<E2Clazz> originClazzes;
        if (lines.isEmpty()) {
            originRecordIds = List.of();
            originRecords = List.of();
            transferIds = List.of();
            transfers = List.of();
            originClazzIds = List.of();
            originClazzes = List.of();
        } else {
            originRecordIds = lines.stream().map(JournalTransferLine::getOriginStudentRecordId).toList();
            originRecords = studentRecordMapper.selectList(new QueryWrapper<StudentRecord>()
                    .lambda()
                    .in(StudentRecord::getId, originRecordIds));
            transferIds = lines.stream().map(JournalTransferLine::getJournalTransferId).toList();
            transfers = journalTransferMapper.selectList(new QueryWrapper<JournalTransfer>()
                    .lambda()
                    .in(JournalTransfer::getId, transferIds));
            originClazzIds = lines.stream().distinct().map(JournalTransferLine::getOriginClazzId).toList();
            originClazzes = e2ClazzMapper.selectList(new QueryWrapper<E2Clazz>()
                    .lambda()
                    .in(E2Clazz::getClazzId, originClazzIds));
        }
        var studentRecordIds = records.stream().map(StudentRecord::getId).collect(Collectors.toList()); // lines.stream().map(JournalTransferLine::getStudentRecordId).toList();
        var targetRecordJournals = journalTransferMapper.findLineByOriginStudentRecordId(studentRecordIds);
        var relationshipMap = recordRelationships.stream().collect(Collectors.toMap(StudentRecordStudentRelationship::getStudentRecordId, x -> x));
        var studentMap = students.stream().collect(Collectors.toMap(Student::getId, x -> x));
        var trasnferMap = transfers.stream().collect(Collectors.toMap(JournalTransfer::getId, x -> x));
        var lineMap = lines.stream().collect(Collectors.toMap(JournalTransferLine::getStudentRecordId, x -> x));
        var originRecordMap = originRecords.stream().collect(Collectors.toMap(StudentRecord::getId, x -> x));
        var targetRecordJournalMap = targetRecordJournals.stream().collect(Collectors.toMap(JournalTransferAndLineId::getOriginStudentRecordId, x -> x));
        var originClazzMap = originClazzes.stream().collect(Collectors.toMap(E2Clazz::getClazzId, x -> x));
        var result = new ArrayList<SuspendStudent>(records.size());
        for (StudentRecord record : records) {
            var relationship = relationshipMap.get(record.getId());
            var student = studentMap.get(relationship.getStudentId());
            var line = lineMap.get(record.getId());
            if (line != null) {
                var transfer = trasnferMap.get(line.getJournalTransferId());
                var originRecord = originRecordMap.get(line.getOriginStudentRecordId());
                var originClazz = originClazzMap.get(line.getOriginClazzId());
                var targetRecordJournal = targetRecordJournalMap.get(record.getId());
                var account = employeeService.findOneByEmail(transfer.getCreatedBy());
                var vo = new SuspendStudent();
                vo.setJournalTransferId(transfer.getId());
                vo.setJournalTransferLineId(line.getId());
                vo.setCreatedBy(transfer.getCreatedBy());
                if (account != null) {
                    vo.setCreatedByName(account.getName());
                    vo.setCreatedByAndName(formatAccount(account, transfer.getCreatedBy()));
                }
                vo.setCreatedDatetime(transfer.getCreatedDatetime());
                vo.setTargetClazzId(transfer.getTargetClazzId());
                vo.setTargetClazzName(transfer.getTargetClazzName());
                vo.setTargetClazzPrice(transfer.getTargetClazzPrice());
                vo.setTargetClazzSubjectId(transfer.getTargetClazzSubjectId());
                vo.setTargetClazzSubjectName(transfer.getTargetClazzSubjectName());
                vo.setTargetClazzCampusId(transfer.getTargetClazzCampusId());
                vo.setTargetClazzCampusName(transfer.getTargetClazzCampusName());
                vo.setTargetClazzModelId(transfer.getTargetClazzModelId());
                vo.setTargetClazzModelName(transfer.getTargetClazzModelName());
                vo.setTargetClazzInDate(line.getTargetClazzInDate());
                vo.setOriginClazzUpgradeable(originClazz.getUpgradeable());
                vo.setOriginStudentRecordId(line.getOriginStudentRecordId());
                vo.setStudentId(student.getId());
                vo.setStudentNumber(student.getNumber());
                vo.setStudentName(student.getName());
                vo.setOriginClazzId(line.getOriginClazzId());
                vo.setOriginClazzName(line.getOriginClazzName());
                vo.setOriginClazzInDate(line.getOriginClazzInDate());
                vo.setOriginClazzOutDate(line.getOriginClazzOutDate());
                vo.setReasonCode(line.getReasonCode());
                vo.setReasonDescription(line.getReasonDescription());
                vo.setOriginClazzSettlementAmount(line.getOriginClazzSettlementAmount());
                vo.setOriginClazzSettlementAmountFormatted(formatPrice(line.getOriginClazzSettlementAmount()));
                vo.setOriginClazzSettlementDays(line.getOriginClazzSettlementDays());
                vo.setRemark(transfer.getRemark());
                if (originRecord != null) {
                    vo.setStudentRecordId(record.getId());
                    vo.setAmountDue(originRecord.getAmountDue());
                    vo.setAmountDueFormatted(formatPrice(originRecord.getAmountDue()));
                    vo.setPaidAmount(originRecord.getPaidAmount());
                    vo.setPaidAmountFormatted(formatPrice(originRecord.getPaidAmount()));
                    vo.setTransferAmount(line.getTargetClazzTransferAmount());
                    vo.setTransferAmountFormatted(formatPrice(line.getTargetClazzTransferAmount()));
                    vo.setPendingAmount(originRecord.getAmountDue() - originRecord.getPaidAmount());
                    vo.setPendingAmountFormatted(formatPrice(vo.getPendingAmount()));
                    if (targetRecordJournal != null) {
                        vo.setTargetStudentJournalTransferId(targetRecordJournal.getJournalTransferId());
                        vo.setTargetStudentJournalTransferLineId(targetRecordJournal.getJournalTransferLineId());
                    }
                    vo.setOriginClazzTypeId(originClazz.getClazzTypeId());
                    vo.setOriginClazzTypeName(originClazz.getClazzTypeName());
                    vo.setOriginClazzPredictionGraduationDate(originClazz.getPredictionGraduationDate());
                    vo.setOriginClazzGraduationDate(originClazz.getGraduationDate());
                } else {
                    var currentClazz = targetClazz;
                    if (currentClazz == null) {
                        currentClazz = studentRecordClazz(record);
                    }
                    vo.setStudentRecordId(record.getId());
                    vo.setAmountDue(record.getAmountDue());
                    vo.setAmountDueFormatted(formatPrice(record.getAmountDue()));
                    vo.setPaidAmount(record.getPaidAmount());
                    vo.setPaidAmountFormatted(formatPrice(record.getPaidAmount()));
                    vo.setTransferAmount(record.getTransferFromAmount());
                    vo.setTransferAmountFormatted(formatPrice(record.getTransferFromAmount()));
                    vo.setPendingAmount(record.getBalanceAmountDue());
                    vo.setPendingAmountFormatted(formatPrice(record.getBalanceAmountDue()));
                    if (targetRecordJournal != null) {
                        vo.setTargetStudentJournalTransferId(targetRecordJournal.getJournalTransferId());
                        vo.setTargetStudentJournalTransferLineId(targetRecordJournal.getJournalTransferLineId());
                    }
                    vo.setOriginClazzTypeId(currentClazz.getClazzTypeId());
                    vo.setOriginClazzTypeName(currentClazz.getClazzTypeName());
                    vo.setOriginClazzPredictionGraduationDate(currentClazz.getPredictionGraduationDate());
                    vo.setOriginClazzGraduationDate(currentClazz.getGraduationDate());
                }
                if (targetClazz != null) {
                    vo.setTargetClazzPredictionGraduationDate(targetClazz.getPredictionGraduationDate());
                    vo.setTargetClazzGraduationDate(targetClazz.getGraduationDate());
                    vo.setTargetClazzStartDate(targetClazz.getStartDate());
                }
                result.add(vo);
            } else {
                var currentClazz = targetClazz;
                if (currentClazz == null) {
                    currentClazz = studentRecordClazz(record);
                }
                var targetRecordJournal = targetRecordJournalMap.get(record.getId());
                var vo = new SuspendStudent();
                vo.setJournalTransferId(null);
                vo.setJournalTransferLineId(null);
                vo.setCreatedBy(record.getCreatedBy());
                var account = employeeService.findOneByEmail(record.getCreatedBy());
                if (account != null) {
                    vo.setCreatedByName(account.getName());
                    vo.setCreatedByAndName(formatAccount(account, record.getCreatedBy()));
                }
                vo.setCreatedDatetime(record.getCreatedDatetime());
                vo.setTargetClazzId(currentClazz.getClazzId());
                vo.setTargetClazzName(currentClazz.getClazzName());
                // vo.setTargetClazzPrice(transfer.getTargetClazzPrice());
                vo.setTargetClazzSubjectId(currentClazz.getSubjectId());
                vo.setTargetClazzSubjectName(currentClazz.getSubjectName());
                vo.setTargetClazzCampusId(currentClazz.getSchoolCode());
                vo.setTargetClazzCampusName(currentClazz.getSchoolName());
                vo.setTargetClazzModelId(currentClazz.getTeachingModel());
                // vo.setTargetClazzModelName(transfer.getTargetClazzModelName());
                vo.setTargetClazzInDate(record.getInDate().toLocalDate());
                vo.setOriginClazzUpgradeable(currentClazz.getUpgradeable());
                vo.setOriginStudentRecordId(null);
                vo.setStudentId(student.getId());
                vo.setStudentNumber(student.getNumber());
                vo.setStudentName(student.getName());
                vo.setOriginClazzId(null);
                vo.setOriginClazzName(null);
                vo.setOriginClazzInDate(null);
                vo.setOriginClazzOutDate(null);
                vo.setReasonCode(null);
                vo.setReasonDescription(null);
                vo.setOriginClazzSettlementAmount(null);   // 结算金额是上个班的结算金额，但是这里很可能找不到上个班，所以这里的结算金额写0
                vo.setOriginClazzSettlementAmountFormatted(formatPrice(0));
                vo.setOriginClazzSettlementDays(0);
                vo.setRemark("");
                vo.setStudentRecordId(record.getId());
                vo.setAmountDue(record.getAmountDue());
                vo.setAmountDueFormatted(formatPrice(record.getAmountDue()));
                vo.setPaidAmount(record.getPaidAmount());
                vo.setPaidAmountFormatted(formatPrice(record.getPaidAmount()));
                vo.setTransferAmount(record.getTransferFromAmount());
                vo.setTransferAmountFormatted(formatPrice(record.getTransferFromAmount()));
                vo.setPendingAmount(record.getBalanceAmountDue());
                vo.setPendingAmountFormatted(formatPrice(record.getBalanceAmountDue()));
                if (targetRecordJournal != null) {
                    vo.setTargetStudentJournalTransferId(targetRecordJournal.getJournalTransferId());
                    vo.setTargetStudentJournalTransferLineId(targetRecordJournal.getJournalTransferLineId());
                }
                vo.setOriginClazzTypeId(currentClazz.getClazzTypeId());
                vo.setOriginClazzTypeName(currentClazz.getClazzTypeName());
                vo.setOriginClazzPredictionGraduationDate(currentClazz.getPredictionGraduationDate());
                vo.setOriginClazzGraduationDate(currentClazz.getGraduationDate());
                if (targetClazz != null) {
                    vo.setTargetClazzPredictionGraduationDate(targetClazz.getPredictionGraduationDate());
                    vo.setTargetClazzGraduationDate(targetClazz.getGraduationDate());
                    vo.setTargetClazzStartDate(targetClazz.getStartDate());
                }
                result.add(vo);
            }
        }
        return result;
    }

    /**
     * 根据学籍ID找学籍
     *
     * @param studentRecordId 学籍ID
     * @return 学籍
     * @throws IllegalArgumentException 如果没有查找到学籍
     */
    StudentRecord studentRecord(String studentRecordId) {
        var studentRecord = studentRecordMapper.selectById(studentRecordId);
        Assert.notNull(studentRecord, "学籍不存在");
        return studentRecord;
    }

    /**
     * 根据学籍找对应的班级
     *
     * @param studentRecord 学籍
     * @return 班级
     * @throws IllegalArgumentException 如果没有查找到学籍对应班级
     */
    E2Clazz studentRecordClazz(StudentRecord studentRecord) {
        var srcClazz = e2ClazzMapper.selectOne(new QueryWrapper<E2Clazz>()
                .lambda()
                .eq(E2Clazz::getClazzId, studentRecord.getClazzId())
                .last("LIMIT 1"));
        Assert.notNull(srcClazz, "没有找到学籍对应的班级");
        return srcClazz;
    }

    /**
     * 根据原班级找对应的休学班级
     *
     * @param srcClazz 原班级
     * @return 休学班级
     * @throws IllegalArgumentException 如果没有查找到对应休学班级
     */
    E2Clazz suspendClazz(E2Clazz srcClazz) {
        var targetClazz = e2ClazzMapper.selectOne(new QueryWrapper<E2Clazz>()
                .lambda()
                .eq(E2Clazz::getSuspensionFlag, true)
                .eq(E2Clazz::getSchoolCode, srcClazz.getSchoolCode())
                .eq(E2Clazz::getSubjectId, srcClazz.getSubjectId())
                .eq(E2Clazz::getTeachingModel, srcClazz.getTeachingModel())
                .eq(E2Clazz::getClazzTypeId, srcClazz.getClazzTypeId())
                .last("LIMIT 1"));
        Assert.notNull(targetClazz, "没有找到对应的休学班级");
        return targetClazz;
    }

    /**
     * 根据学籍，原班级，出班日期计算金额
     *
     * @param studentRecord 学籍
     * @param srcClazz      原班级
     * @return 休学班级
     */
    SuspendJournalVo setAmounts(
            StudentRecord studentRecord,
            E2Clazz srcClazz,
            LocalDate originClazzOutDate
    ) {
        var result = new SuspendJournalVo();
        final var settlementResult = commonService.calculate(
                JournalTransferType.SUSPENSION,
                studentRecord,
                originClazzOutDate.atTime(0, 0),
                srcClazz
        );
        result.setOriginClazzAmountDueFormatted(formatPrice(studentRecord.getAmountDue()));
        result.setOriginClazzServiceDays(settlementResult.getServiceDays());
        result.setOriginClazzActualSettlementDays(settlementResult.getSettlementDays());
        result.setOriginClazzActualSettlementAmountFormatted(formatPrice(settlementResult.getSettlementAmount()));
        result.setPaidAmountFormatted(formatPrice(studentRecord.getPaidAmount()));
        result.setFreezeAmountFormatted(formatPrice(settlementResult.getTransferAmount()));
        return result;
    }

    @NotNull
    private static JournalTransferCreateBo getJournalTransferCreateBo(EditSuspendJournalParams params, E2Clazz targetClazz) {
        var bo = new JournalTransferCreateBo();
        bo.setId(params.getId());
        bo.setType(JournalTransferType.SUSPENSION);
        bo.setState(params.getState());
        bo.setTargetClazzCampusId(targetClazz.getSchoolCode());
        bo.setTargetClazzModelId(targetClazz.getTeachingModel());
        bo.setSpecialFlag(params.getSpecialFlag());
        bo.setRemark(params.getRemark());
        bo.setTargetClazzSubjectId(targetClazz.getSubjectId());
        bo.setTargetClazzId(targetClazz.getClazzId());
        bo.setReasonCode(params.getReasonCode());
        bo.setDirection(params.getDirection());
        return bo;
    }

    private JournalTransferLine getJournalTransferCreateBoSingleLine(
            EditSuspendJournalParams params,
            StudentRecord studentRecord,
            E2Clazz srcClazz,
            JournalTransferReasonVDTO reasonByCode,
            E2Clazz targetClazz
    ) {
        var targetClazzOriginalAmount = tuitionFeeService.findTuition(targetClazz.getClazzId(), null).multiply(new BigDecimal(100)).intValue();
        var journalTransferLine = new JournalTransferLine();
        if ("休学".equals(params.getDirection())) {
            final var settlementResult = commonService.calculate(
                    JournalTransferType.SUSPENSION,
                    studentRecord,
                    params.getOriginClazzOutDate().atTime(0, 0),
                    srcClazz
            );
            Assert.notNull(params.getOriginClazzOutDate(), "转出班出班日期不能为空");
            Assert.notNull(params.getTargetClazzInDate(), "转入班入班日期不能为空");
            journalTransferLine.setOriginClazzOutDate(params.getOriginClazzOutDate());
            journalTransferLine.setTargetClazzInDate(params.getTargetClazzInDate());
            journalTransferLine.setOriginStudentRecordId(studentRecord.getId());
            journalTransferLine.setOriginClazzId(srcClazz.getClazzId());
            journalTransferLine.setOriginClazzName(srcClazz.getClazzName());
            journalTransferLine.setOriginClazzInDate(studentRecord.getInDate().toLocalDate());
            journalTransferLine.setOriginClazzOriginalAmount(studentRecord.getPrice());
            journalTransferLine.setOriginClazzDiscountAmount(studentRecord.getDiscountAmount());
            journalTransferLine.setOriginClazzAmountDue(studentRecord.getAmountDue());
            journalTransferLine.setOriginActualAmountDue(studentRecord.getActualAmountDue());
            journalTransferLine.setOriginClazzSettlementAmount(settlementResult.getSettlementAmount());
            journalTransferLine.setOriginClazzActualSettlementAmount(settlementResult.getSettlementAmount());
            journalTransferLine.setOriginClazzServiceDays(settlementResult.getServiceDays());
            journalTransferLine.setOriginClazzSettlementDays(settlementResult.getSettlementDays());
            journalTransferLine.setOriginClazzActualSettlementDays(settlementResult.getSettlementDays());
            journalTransferLine.setTargetClazzOriginalAmount(targetClazzOriginalAmount);
            journalTransferLine.setTargetClazzDiscountAmount(0);
            journalTransferLine.setTargetClazzAmountDue(studentRecord.getActualAmountDue() - settlementResult.getSettlementAmount());
            journalTransferLine.setTargetClazzActualAmountDue(studentRecord.getActualAmountDue() - settlementResult.getSettlementAmount()); // 同学科转班，所以是剩下的钱
            journalTransferLine.setTargetClazzTransferAmount(settlementResult.getTransferAmount());
            journalTransferLine.setPriceDifferenceFlag(Boolean.TRUE);
            journalTransferLine.setPriceDifferenceAmount(studentRecord.getPrice());
            // journalTransferLine.setState(line.getState());
            // journalTransferLine.setStudentRecordId(line.getStudentRecordId());
            journalTransferLine.setTenantId(TenantInfo.DEFAULT_TENANT_ID);
            journalTransferLine.setSubtenantId(TenantInfo.DEFAULT_SUBTENANT_ID);
            journalTransferLine.setCreatedBy(AccountHolder.getRequiredAccount());
            journalTransferLine.setCreatedDatetime(LocalDateTime.now());
            journalTransferLine.setDeletedFlag(Boolean.FALSE);
            if (reasonByCode != null) {
                journalTransferLine.setReasonCode(reasonByCode.getCode());
                journalTransferLine.setReasonDescription(reasonByCode.getName());
            }
        } else if ("休学归来".equals(params.getDirection())) {
            final var settlementResult = commonService.calculate(
                    JournalTransferType.SUSPENSION,
                    studentRecord,
                    params.getTargetClazzInDate().minusDays(1).atTime(0, 0),
                    srcClazz
            );
            Assert.notNull(params.getOriginClazzOutDate(), "转出班出班日期不能为空");
            Assert.notNull(params.getTargetClazzInDate(), "转入班入班日期不能为空");
            journalTransferLine.setOriginClazzOutDate(params.getOriginClazzOutDate());
            journalTransferLine.setTargetClazzInDate(params.getTargetClazzInDate());
            journalTransferLine.setOriginStudentRecordId(studentRecord.getId());
            journalTransferLine.setOriginClazzId(srcClazz.getClazzId());
            journalTransferLine.setOriginClazzName(srcClazz.getClazzName());
            journalTransferLine.setOriginClazzInDate(studentRecord.getInDate().toLocalDate());
            journalTransferLine.setOriginClazzOriginalAmount(studentRecord.getPrice());
            journalTransferLine.setOriginClazzDiscountAmount(studentRecord.getDiscountAmount());
            journalTransferLine.setOriginClazzAmountDue(studentRecord.getAmountDue());
            journalTransferLine.setOriginActualAmountDue(studentRecord.getActualAmountDue());
            journalTransferLine.setOriginClazzSettlementAmount(settlementResult.getSettlementAmount());
            journalTransferLine.setOriginClazzActualSettlementAmount(settlementResult.getSettlementAmount());
            journalTransferLine.setOriginClazzServiceDays(settlementResult.getServiceDays());
            journalTransferLine.setOriginClazzSettlementDays(settlementResult.getSettlementDays());
            journalTransferLine.setOriginClazzActualSettlementDays(settlementResult.getSettlementDays());
            journalTransferLine.setTargetClazzOriginalAmount(targetClazzOriginalAmount);
            journalTransferLine.setTargetClazzDiscountAmount(0);
            journalTransferLine.setTargetClazzAmountDue(studentRecord.getActualAmountDue() - settlementResult.getSettlementAmount());
            journalTransferLine.setTargetClazzActualAmountDue(studentRecord.getActualAmountDue() - settlementResult.getSettlementAmount());
            journalTransferLine.setTargetClazzTransferAmount(settlementResult.getTransferAmount());
            journalTransferLine.setPriceDifferenceFlag(Boolean.TRUE);
            journalTransferLine.setPriceDifferenceAmount(studentRecord.getPrice());
            journalTransferLine.setTenantId(TenantInfo.DEFAULT_TENANT_ID);
            journalTransferLine.setSubtenantId(TenantInfo.DEFAULT_SUBTENANT_ID);
            journalTransferLine.setCreatedBy(AccountHolder.getRequiredAccount());
            journalTransferLine.setCreatedDatetime(LocalDateTime.now());
            journalTransferLine.setDeletedFlag(Boolean.FALSE);
            if (reasonByCode != null) {
                journalTransferLine.setReasonCode(reasonByCode.getCode());
                journalTransferLine.setReasonDescription(reasonByCode.getName());
            }
        } else {
            throw new IllegalArgumentException("direction参数不正确, 只能为休学或休学归来, 不能为:" + params.getDirection());
        }
        return journalTransferLine;
    }

}
